home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / v8n21.arc / PCMANAGE.C < prev    next >
Text File  |  1989-11-11  |  23KB  |  1,036 lines

  1. /****************************************************************************
  2. *   PCMANAGE - Compresses files not accessed within a certain time frame
  3. *
  4. *    Copyright (c) 1989 by Ziff Communications Co.
  5. *       Program by Ross M. Greenberg
  6. ****************************************************************************/
  7.  
  8. /*********
  9. **            Standard Include Files
  10. **********/
  11.  
  12. #include    <stdio.h>
  13. #include    <dir.h>
  14. #include    <dos.h>
  15. #include    <fcntl.h>
  16. #include    <stat.h>
  17. #include    <string.h>
  18. #include    <conio.h>
  19. #include    <io.h>
  20.  
  21. #define    FALSE    0
  22. #define    TRUE    !FALSE
  23. #define    ESCAPE    0x1b
  24. #define min(a,b) ((a) < (b) ? (a) : (b))
  25. #define max(a,b) ((a) > (b) ? (a) : (b))
  26.  
  27. /*********
  28. **            Define the Box character set
  29. **********/
  30.  
  31. #define    UPPER_LEFT    0xd5
  32. #define    UPPER_RIGHT    0xb8
  33. #define    LOWER_LEFT    0xd4
  34. #define    LOWER_RIGHT    0xbe
  35. #define    HORIZONTAL    0xcd
  36. #define    VERTICAL    0xb3
  37. #define    LEFT_CROSS    0xc6
  38. #define    RIGHT_CROSS    0xb5
  39.  
  40. /*********
  41. **        Turn DCOMPRES On or OFF
  42. **********/
  43.  
  44. #define    ON    0
  45. #define    OFF    1
  46.  
  47.  
  48. #define    RESET    -2
  49. #define    CLOSE    -3
  50.  
  51. /*********
  52. **        Status Byte in the Index File
  53. **********/
  54.  
  55. #define    NORMAL        0
  56. #define    COMPRESSED    1
  57. #define    DELETED        2
  58. #define    SKIP        3
  59. #define    TESTING        4
  60. #define    ERROR        5
  61. #define    LOW        6
  62.  
  63. #define    MAX_FLEN    14
  64. #define    MAX_PLEN    64
  65. #define    MAX_DISK    3
  66.  
  67. #define    MAX_FILES    100
  68. #define    TBUF_SIZE    128
  69.  
  70. /*********
  71. **        Structure of the the Index file
  72. **********/
  73. struct    INDEX
  74.     {
  75.         int        fname_len;
  76.         char        filename[MAX_FLEN];
  77.         unsigned    left_ptr;
  78.         unsigned    right_ptr;
  79.         struct        date    da;
  80.         struct        time    ti;
  81.         char        status;
  82.         char        access_cnt;
  83.     };
  84.  
  85. /*********
  86. **        Don't separate these next two fields
  87. **********/
  88. int        num_files;
  89. char        path_name[MAX_PLEN + MAX_FLEN];
  90. struct        INDEX    *index;
  91.  
  92.  
  93. char        only_dir[MAX_PLEN];
  94. char        *screen;
  95. int        days;
  96. int        min_percent;
  97. int        min_bytes;
  98. int        install_flag;
  99. int        install_window;
  100.  
  101. #define    EMPTY    -1
  102. #define    IN_TABLE    -1
  103.  
  104.  
  105. /*********
  106. **        The actual code table
  107. **********/
  108. struct    TABLE
  109.     {
  110.         int    code;
  111.         char    suffix;
  112.     };
  113.  
  114. #define    MAX_CODE    4096
  115. #define    RESET_CODE    (MAX_CODE - 1)
  116.  
  117. struct    TABLE    *table;
  118. int        reset;
  119. int        codesused;
  120. unsigned    oldcode;
  121.  
  122. int        *hashtable;
  123. long        total = 0;
  124. long        in_cnt = 0;
  125. long        out_cnt = 0;
  126. long        total_in_cnt = 0;
  127. long        total_out_cnt = 0;
  128. int        percent;
  129. int        dirty_bit;
  130. int        scan_cnt;
  131. int        tot_examine;
  132. int        tot_skipped;
  133. int        tot_compressed;
  134. int        tot_bypassed;
  135. int        tot_files;
  136. int        tot_directories;
  137.  
  138.  
  139.  
  140. char        *copyright = "PCMANAGE, Copyright (c) 1989 by Ziff Communications Co. \
  141. All Rights Reserved.";
  142. char        *program = "Program by Ross M. Greenberg";
  143. char        start_path[MAX_PLEN + MAX_DISK];
  144.  
  145. int        exclude_cnt = 3;
  146. char        *exclude_list[MAX_FILES] =
  147.             {"*INDEX.CMP",
  148.              "*DCOMPRES.COM",
  149.              "*PCMANAGE.EXE"
  150.             };
  151.  
  152.  
  153. /*********
  154. **        Code Starts Here
  155. **********/
  156.  
  157.  
  158. void center(int row, char *ptr)
  159. {
  160.     gotoxy(40 - strlen(ptr)/2, row);
  161.     cprintf(ptr);
  162. }
  163.  
  164. void draw_screen()
  165. {
  166. char    tmp_buf[TBUF_SIZE];
  167. int    i;
  168. char    *ptr;
  169.  
  170.     textcolor(WHITE);
  171.     textbackground(BLUE);
  172.     clrscr();
  173.     center(1, copyright);        /* the Ziff copyright notice */
  174.     center(2, program);        /* Program ID and author */
  175.     ptr = tmp_buf;
  176.     for (i=1; i < 80 ; i++)
  177.         *ptr++ = HORIZONTAL;    /* make a horizontal line */
  178.     tmp_buf[0] = UPPER_LEFT;    /* fill in the corners */
  179.     tmp_buf[79] = UPPER_RIGHT;
  180.     tmp_buf[80] = 0;
  181.     gotoxy(1,3);
  182.     cprintf(tmp_buf);        /* top line */
  183.     tmp_buf[0] = LOWER_LEFT;    /* make it a bottom line */
  184.     tmp_buf[79] = LOWER_RIGHT;
  185.     gotoxy(1, 24);
  186.     cprintf(tmp_buf);        /* and draw it */
  187.  
  188.     for (i = 4 ; i < 24 ; i++)    /* draw down each side */
  189.     {
  190.         gotoxy(1, i);
  191.         putch(VERTICAL);
  192.         gotoxy(80, i);
  193.         putch(VERTICAL);
  194.     }
  195.  
  196.     tmp_buf[0] = LEFT_CROSS;    /* cross boxes */
  197.     tmp_buf[79] = RIGHT_CROSS;
  198.     gotoxy(1, 7);
  199.     cprintf(tmp_buf);        /* horizontal line */
  200.     gotoxy(1, 9);
  201.     cprintf(tmp_buf);        /* horizontal line */
  202.     if    (!install_flag)
  203.     {
  204.         gotoxy(2,10);
  205.         cprintf("  Filename     Last Access    Status     Filename     Last Access    Status");
  206.         gotoxy(1, 16);
  207.         cprintf(tmp_buf);        /* horizontal line */
  208.  
  209.         gotoxy(2, 4);
  210.         cprintf("Only files without access in the past %d days and with a compression ratio", days);
  211.         gotoxy(2,5);
  212.         cprintf("of at least %d%% and %d bytes will be compressed.", min_percent, min_bytes);
  213.     }
  214.     else
  215.     {
  216.         gotoxy(2,4);
  217.         cprintf("Accessing all files.");
  218.     }
  219.     gotoxy(2,6);
  220.     cprintf("Starting path is:  %s", start_path);
  221.  
  222. }
  223.  
  224. /*********
  225. **        Draw 'N' graphics characters across
  226. **********/
  227. void bar_chart(int num_bars, char bar_char)
  228. {
  229. char    tmp_buf[TBUF_SIZE];
  230. char    *ptr = tmp_buf;
  231.  
  232.     if    (!num_bars)
  233.         return;
  234.     num_bars = min(num_bars, 50);
  235.     while(num_bars--)
  236.         *ptr++ = bar_char;
  237.     *ptr = 0;
  238.     cprintf(tmp_buf);
  239. }
  240.  
  241. /*********
  242. **    Open the exclude file and load the contents into an array.
  243. **    Maximum of 20 members in the arrray, three already populated.
  244. **********/
  245. void do_exclude()
  246. {
  247. FILE    *fp;
  248. char    tmp_buf[TBUF_SIZE];
  249. char    *ptr;
  250.  
  251.     if    ((fp = fopen("C:\\DCOMPRES.EXL", "r")) == (FILE *)NULL)
  252.         return;
  253.     while    (fgets(tmp_buf, TBUF_SIZE, fp) && exclude_cnt < 20)
  254.     {
  255.             ptr = (char *)malloc(strlen(tmp_buf) + 1);
  256.                 tmp_buf[strlen(tmp_buf) - 1] = 0;
  257.         strcpy(ptr, tmp_buf);
  258.          strupr(ptr);
  259.                 exclude_list[exclude_cnt++] = ptr;
  260.     }
  261.     fclose(fp);
  262. }
  263.  
  264. /*********
  265. **    Tell DCOMPRES to turn on or off.
  266. **********/
  267.  
  268. void sq_toggle(int flag)
  269. {
  270. union    REGS    regset;
  271.  
  272.     regset.h.ah = 0xdc;
  273.     regset.x.dx = flag;
  274.     intdos(®set, ®set);
  275. }
  276.  
  277.  
  278. /*********
  279. **    If a control C or Break is hit, turn DCOMPRES back on
  280. **********/
  281. void c_break(void)
  282. {
  283.     printf("Control-C Hit....Aborting\n");
  284.     sq_toggle(ON);
  285.     exit(1);
  286. }
  287.  
  288.  
  289. char    *do_stat(int s)
  290. {
  291.     switch    (s)
  292.     {
  293.         case    NORMAL:
  294.             return("Normal    ");
  295.  
  296.         case    DELETED:
  297.             return("Deleted   ");
  298.  
  299.         case    COMPRESSED:
  300.             return("Compressed");
  301.  
  302.         case    ERROR:
  303.             return("Error     ");
  304.  
  305.         case    TESTING:
  306.             return("Testing   ");
  307.  
  308.         case    SKIP:
  309.             return("Skipping  ");
  310.  
  311.         case    LOW:
  312.             return("No Compres");
  313.  
  314.         default:
  315.             return("Unknown   ");
  316.     }
  317. }
  318.  
  319. /*********
  320. **    Process each entry in the index file
  321. **
  322. **  1.  Slide down the tree as left as you can via recursion.
  323. **  2.  When the left end of a branch is hit, output the file stuff
  324. **      formatted pretty.
  325. **  3.  If not a NORMAL file, skip it.
  326. **  4.  Otherwise, check if "outdated". Skip if not.
  327. **  5.  Otherwise, do the compression on the file.
  328. **  6.  Output the status when starting, update it when done.
  329. **  7.  Process the rest of the tree - the righthand side.
  330. **  9.  Return
  331. **********/
  332.  
  333. void
  334. scan_files(int cnt)
  335. {
  336. int    xx;
  337. int    yy;
  338. int    stat;
  339.  
  340.  
  341.     if    (kbhit())
  342.     {
  343.         if    (getch() == ESCAPE)
  344.             return;
  345.     }
  346.     cnt--;
  347.     if    (index[cnt].left_ptr)
  348.     {
  349.          scan_files(index[cnt].left_ptr);
  350.     }
  351.     cprintf("%-12s %02d:%02d %02d/%02d/%02d ",
  352.         index[cnt].filename,
  353.         index[cnt].ti.ti_hour,
  354.         index[cnt].ti.ti_min,
  355.         index[cnt].da.da_mon,
  356.         index[cnt].da.da_day,
  357.         index[cnt].da.da_year - 1900);
  358.     xx = wherex();
  359.     yy = wherey();
  360.     cprintf("%s", do_stat(index[cnt].status));
  361.  
  362.  
  363.     if    (index[cnt].status == COMPRESSED)
  364.         tot_compressed++;
  365.     else
  366.     if    (index[cnt].status == LOW)
  367.         tot_bypassed++;
  368.     else
  369.     if    (index[cnt].status == NORMAL &&
  370.             check_date(&index[cnt]) >= days)
  371.     {
  372.         gotoxy(xx, yy);
  373.         cprintf(do_stat(TESTING));
  374.         stat =     compress(&index[cnt]);
  375.         window(2, 11, 79, 15);
  376.         gotoxy(xx, yy);
  377.         cprintf("%s", do_stat(stat));
  378.         }
  379.  
  380.     if    (!(++scan_cnt % 2))
  381.         putch('\n');
  382.     else
  383.         putch(VERTICAL);
  384.  
  385.     if    (index[cnt].right_ptr)
  386.     {
  387.         scan_files(index[cnt].right_ptr);
  388.     }
  389.     return;
  390. }
  391.  
  392.  
  393.  
  394. /*********
  395. **    Process the index file
  396. **
  397. **  1.  Read the number of files in the index
  398. **  2.  Read the directory path of the index in.
  399. **  3.  If no match, then something weird happened. Return.
  400. **  4.  Read the whole index file into memory
  401. **  5.  Call scan_files the index file entries.
  402. **  6.  If there was a change made, then write the index back out.
  403. **  7.    Close the index file and return.
  404. **
  405. **********/
  406.  
  407.  
  408. void do_index(char *dir)
  409. {
  410. int    fd;
  411. char    filename[MAX_PLEN+MAX_FLEN];
  412. int    stat;
  413. char    tmp;
  414. char    tmp_buf[TBUF_SIZE];
  415. int    attrb;
  416.  
  417.     window(1, 1, 80, 25);
  418.     sprintf(filename, "%s\\INDEX.CMP", dir);
  419.     attrb = _chmod(filename, NULL);
  420.     _chmod(filename, 1, attrb & ~FA_RDONLY);
  421.     if    ((fd = open(filename, O_RDWR | O_BINARY)) == -1)
  422.         return;
  423.  
  424.     _read(fd, &num_files, 2);
  425.     _read(fd, &path_name, MAX_PLEN + MAX_FLEN);
  426.     strcpy(only_dir, dir);
  427.     strcat(only_dir, "\\");
  428.     if    (strncmp(dir, path_name, strlen(dir)))
  429.         return;
  430.  
  431.     _read(fd, index, sizeof(struct INDEX) * 100);
  432.     gotoxy(2, 8);
  433.     sprintf(tmp_buf, "%d Files in %s", num_files, dir);
  434.     cprintf("%-78.78s", tmp_buf);
  435.     dirty_bit = FALSE;
  436.     window(2, 11, 79, 15);
  437.     clrscr();
  438.     gotoxy(1,1);
  439.     scan_cnt = 0;
  440.     scan_files(1);
  441.     if    (dirty_bit)
  442.         {
  443.         lseek(fd, (long)(2 + MAX_PLEN + MAX_FLEN), 0);
  444.         _write(fd, (char *)index, sizeof(struct INDEX) * 100);
  445.     }
  446.     close(fd);
  447.     _chmod(filename, attrb);
  448. }
  449.  
  450.  
  451. /*********
  452. **    Process a directory
  453. **
  454. **  1.  Do the index file, if any, in the argument directory
  455. **  2.  Create a template for FindFirst and FindNext calls
  456. **  3.  Find the first file.  If none match, then return
  457. **  4.  Make sure we're not processing `.' or `..' as a file.
  458. **  5.  If a directory, then process it
  459. **  6.  Go on to next file.  When no more files, return
  460. **
  461. **********/
  462. do_dir(char *root)
  463. {
  464. struct    ffblk    ffblk;
  465. int    done;
  466. char    template[MAX_PLEN];
  467. char    dir_template[MAX_PLEN];
  468. char    tmp_dir[MAX_PLEN + MAX_FLEN];
  469. int    install_fd;
  470.  
  471.     if    (exclude(root))
  472.         return(TRUE);
  473.     if    (!install_flag)
  474.         do_index(root);
  475.         else
  476.     {
  477.         tot_directories++;
  478.         if    (!install_window)
  479.         {
  480.             install_window = TRUE;
  481.             window(2, 10, 79, 23);
  482.         }
  483.         }
  484.  
  485.     strcpy(dir_template, root);
  486.     strcat(dir_template, "\\");
  487.     sprintf(template, "%s*.*", dir_template);
  488.     done = findfirst(template, &ffblk, FA_DIREC );
  489.     while    (!done)
  490.     {
  491.         if    (kbhit())
  492.         {
  493.             if    (getch() == ESCAPE)
  494.                 return(FALSE);
  495.         }
  496.         if    (strncmp(ffblk.ff_name, ".", 1) && strncmp(ffblk.ff_name, "..", 2))
  497.         {
  498.             sprintf(tmp_dir, "%s%s", dir_template, ffblk.ff_name);
  499.             if    (ffblk.ff_attrib & FA_DIREC)
  500.             {
  501.                 if    (!do_dir(tmp_dir))
  502.                     return(FALSE);
  503.             }
  504.             else
  505.             if    (install_flag)
  506.             {
  507.                 if    (!exclude(tmp_dir))
  508.                 {
  509.                     if    ((install_fd = open(tmp_dir, O_RDONLY)) != -1)
  510.                     {
  511.                         tot_files++;
  512.                         cprintf("Accessing: %s\n", tmp_dir);
  513.                         close(install_fd);
  514.                     }
  515.                  }
  516.             }
  517.         }
  518.         done = findnext(&ffblk);
  519.     }
  520.     return(TRUE);
  521. }
  522.  
  523. /*********
  524. **    Stuff a code into the buffer
  525. **
  526. **  1.  If a reset, initialize everything, return
  527. **  2.  If this is a `normal' code entry (we're not closing the file),
  528. **      create the code as a nibble and a byte or byte and a nibble.
  529. **  3.  If we're closing the file, or the buffer is full, then close the
  530. **      buffer if required, output the buffer and start to reset.
  531. **
  532. **********/
  533.  
  534. void stuff(int fd, unsigned code, int flag)
  535. {
  536. static    char        buffer[750];        /*enough for 500 entries*/
  537. static    char        *buffer_end = buffer + 750;
  538. static    char        *p = buffer;
  539. static    unsigned     hold;
  540.  
  541.     if    (flag == RESET)
  542.     {
  543.         buffer_end = buffer + 750;
  544.         p = buffer;
  545.         hold = 0;
  546.         return;
  547.     }
  548.  
  549.     if    (flag == NORMAL)
  550.     {
  551.         if    (!(hold & 0x100))
  552.         {
  553.             *p++ = (code & 0xff0) >> 4;
  554.             hold = (code & 0xf) | 0x100;
  555.             out_cnt++;
  556.         }
  557.         else
  558.         {
  559.             hold &= 0xf;
  560.             *p++ = (hold << 4) | ((code & 0xf00) >> 8);
  561.             *p++ = code & 0xff;
  562.             hold = 0;
  563.             out_cnt += 2;
  564.         }
  565.     }
  566.  
  567.     if    (flag == CLOSE || p >= buffer_end)
  568.     {
  569.         if    (hold & 0x100)
  570.         {
  571.             *p++ = (hold << 4);
  572.             *p = 0;
  573.         }
  574.         _write(fd, &buffer, p - buffer);
  575.         p = buffer;
  576.     }
  577. }
  578.  
  579. /*********
  580. **    Return the next character from the input file
  581. **
  582. **  1.  If a reset, initialize everything, return
  583. **  2.  Allocate bufer memory for the read if we haven't already
  584. **  3.  Read the input file in big chunks as required, returning with
  585. **      FALSE on EOF, else TRUE.  Charaacter de-referenced via argument
  586. **      pointer.
  587. **
  588. **********/
  589.  
  590. get_char(int fd, char *ptr)
  591. {
  592. static    unsigned    cnt = 0;
  593. static    char    *p;
  594. static    char    *ptr2 = NULL;
  595. static  int    size_mem;
  596.  
  597.     if    (fd == RESET)
  598.     {
  599.         cnt = 0;
  600.         return(FALSE);
  601.     }
  602.     if    (!ptr2)
  603.     {
  604.             size_mem = min(coreleft() - 5 * 1024, 32760);
  605.         ptr2 = (char *)malloc(size_mem);
  606.         p = ptr2;
  607.     }
  608.  
  609.     if    (!cnt)
  610.     {
  611.         cnt = _read(fd, ptr2, size_mem);
  612.         if    (!cnt)
  613.             return(FALSE);
  614.         p = ptr2;
  615.     }
  616.     *ptr = *p++;
  617.     cnt--;
  618.     return(TRUE);
  619. }
  620.  
  621. /*********
  622. **    Initialize the table, the "used" counter, etc.
  623. **********/
  624.  
  625. void init_table(void)
  626. {
  627. int    i;
  628.  
  629.     for(i = 0; i < 256 ; i++)
  630.     {
  631.         table[i].code = 0;
  632.         table[i].suffix = (char)i;
  633.     }
  634.     codesused = 256;
  635.     for (i = 0 ; i < MAX_CODE ; i++)
  636.         hashtable[i] = EMPTY;
  637. }
  638.  
  639. void do_usage()
  640. {
  641.     printf(copyright);
  642.     printf("\n\nUsage:\nPCMANAGE -i -d<days> -c<percent> -b<byte_cnt> -p<path>\n\n");
  643.     printf("      -i:         access all files in the specified directory path.\n");
  644.     printf("                  use only the first time you run PCMANAGE.\n\n");
  645.     printf("      <days>:     Compress files that have not been accessed\n");
  646.     printf("                  in <days> or longer.  Default is 7 days.\n\n");
  647.     printf("      <percent>:  Only compress files where the compression\n");
  648.     printf("                  ratio is greater than <percent>. The\n");
  649.     printf("          default is set for 5%%\n");
  650.     printf("      <byte_cnt>: Only compress files saving more than <byte_cnt>\n");
  651.     printf("                  bytes.  Default is one cluster.\n");
  652.     printf("      <path>:     the fully qualified base directory name\n");
  653.     printf("                  to recurse through.\n");
  654. }
  655.  
  656.  
  657. /*********
  658. **    Main program
  659. **
  660. **  1.  Set up default values
  661. **  2.  Process options and arguments
  662. **  3.  Process the exclude file
  663. **  4.  Paint screen and allocate memory
  664. **  5.  Set Control-C handler and turn off DCOMPRES
  665. **  6.  -->Process the directory heirarchy
  666. **  7.  Turn DCOMPRES back on
  667. **  8.  Print stats and exit
  668. **********/
  669.  
  670. void main(int argc, char *argv[])
  671. {
  672. int    cnt;
  673. struct    fatinfo fi;
  674. int    orig_x,
  675.     orig_y;
  676.  
  677.     strcpy(start_path, "C:");
  678.     days = 7;
  679.     min_percent = 10;
  680.     cnt = 1;
  681.  
  682.     while    (argc && argv[cnt][0])
  683.     {
  684.         if    (argv[cnt][0] != '-' && argv[cnt][0] != '/')
  685.                 {
  686.             do_usage();
  687.             exit();
  688.         }
  689.                 switch    (toupper(argv[cnt][1]))
  690.         {
  691.             case    'D':
  692.                 days = atoi(&argv[cnt][2]);
  693.                 if    (days < 1)
  694.                 {
  695.                     printf("Days must be more than one!\n");
  696.                     exit(1);
  697.                 }
  698.                 break;
  699.  
  700.             case    'C':
  701.                 min_percent = atoi(&argv[cnt][2]);
  702.                 if    (min_percent < 5)
  703.                 {
  704.                     printf("Minimum percentage must be greater than 5%%\n");
  705.                     exit(1);
  706.                 }
  707.                 break;
  708.  
  709.             case    'P':
  710.                 strncpy(start_path, &argv[cnt][2], 67);
  711.                 if    (strlen(start_path) == 1)
  712.                     strcpy(start_path, "");
  713.                                 break;
  714.  
  715.             case    'B':
  716.                 min_bytes = atoi(&argv[cnt][2]);
  717.                 if    (min_bytes < 1024)
  718.                 {
  719.                     printf("Minimum compression count must be greater than 1024 bytes\n");
  720.                     exit(1);
  721.                 }
  722.                 break;
  723.  
  724.             case    'I':
  725.                 install_flag = TRUE;
  726.                 break;
  727.  
  728.             default:
  729.                 do_usage();
  730.                 exit(1);
  731.                 }
  732.                 cnt++;
  733.     }
  734.  
  735.     if    (!min_bytes)
  736.     {
  737.         getfat(toupper(*start_path) - '@', &fi);
  738.         min_bytes = fi.fi_sclus * fi.fi_bysec;
  739.     }
  740.  
  741.     index = (struct INDEX *)calloc(100, sizeof(struct INDEX));
  742.     table = (struct TABLE *)calloc(MAX_CODE, sizeof(struct TABLE));
  743.     hashtable = (int *)calloc(MAX_CODE, sizeof(int));
  744.     screen = (char *)calloc(80 * 2 * 25, sizeof(char));
  745.  
  746.     gettext(1, 1, 80, 25, screen);
  747.     orig_x = wherex();
  748.     orig_y = wherey();
  749.     do_exclude();
  750.     draw_screen();
  751.  
  752.     ctrlbrk(c_break);
  753.     if    (!install_flag)
  754.         sq_toggle(OFF);
  755.     do_dir(start_path);
  756.     sq_toggle(ON);
  757.     if    (!install_flag)
  758.     {
  759.         window(2, 17, 79, 23);
  760.         clrscr();
  761.         if    (total_in_cnt)
  762.             percent = (int)((100 * (total_in_cnt - total_out_cnt))/total_in_cnt);
  763.         cprintf("Total compression of %ld bytes, representing a %d%% compression ratio.\n",
  764.             total_in_cnt - total_out_cnt, percent);
  765.         cprintf("Total of %d files examined, %d skipped, %d compressed, %d bypassed.\n",
  766.             tot_examine, tot_skipped, tot_compressed, tot_bypassed);
  767.     }
  768.     else
  769.     {
  770.         window(2, 10, 79, 23);
  771.         clrscr();
  772.         cprintf("A total of %d files accessed in %d directories examined\n", tot_files, tot_directories);
  773.     }
  774.     while    (kbhit())
  775.         getch();
  776.     cprintf("\n\nPress a key to continue.");
  777.     getch();
  778.     window(1,1, 80, 25);
  779.     puttext(1, 1, 80, 25, screen);
  780.     gotoxy(orig_x, orig_y);
  781.     exit(0);
  782. }
  783.  
  784.  
  785.  
  786. /*********
  787. **    Make a hash out of code and suffix
  788. **********/
  789.  
  790.  
  791. unsigned make_hash(unsigned c1, unsigned c2)
  792. {
  793.     return((c1 ^ (c2 << 3)) & 0xfff);
  794. }
  795.  
  796.  
  797. /*********
  798. **    The guts of LZW
  799. **
  800. **  1.  Make a hash out of the last code and current character
  801. **  2.  Find an empty spot in the hashtable or find a matching entry
  802. **  3.  If there's room in the table, add this code-suffix combination
  803. **  4.  Save the code and return the new code.
  804. **********/
  805.  
  806. unsigned get_code(unsigned char c)
  807. {
  808. int    i;
  809. unsigned    output;
  810. int    idx;
  811. unsigned int    hash = make_hash(oldcode, (unsigned)c);
  812.  
  813.     idx = hashtable[hash];
  814.     while (idx != EMPTY)
  815.     {
  816.         if    (table[idx].code == oldcode && table[idx].suffix == c)
  817.         {
  818.             oldcode = idx;
  819.             return(IN_TABLE);
  820.         }
  821.         hash += 101;
  822.         hash %= (MAX_CODE);
  823.         idx = hashtable[hash];
  824.     }
  825.  
  826.     output = oldcode;
  827.  
  828.     if    (codesused < MAX_CODE - 1)
  829.     {
  830.         table[codesused].code = oldcode;
  831.         table[codesused].suffix = c;
  832.         hashtable[hash] = codesused;
  833.         codesused++;
  834.     }
  835.     oldcode = (unsigned)c;
  836.     return(output & 0x0fff);
  837. }
  838.  
  839. /*********
  840. **    LZW main routine
  841. **
  842. **  1.  If the file is on the exclude list, return without processing
  843. **  2.  Open the file and get the file length
  844. **  3.  Open the scratch file and write our tag.  The tag starts with an
  845. **      INT 20, an exit instruction in case a DCOMPRES file is run without
  846. **      DCOMPRES running, no system hang.
  847. **  4.  Initialize everything and read the first character from the file
  848. **  5.  For each character in the file: if the character and last code
  849. **      are already in the table, get the next character and try again.
  850. **      If there isn't room in the table to stuff the new combination,
  851. **      output a reset code, reset the table and continue.
  852. **  6.  Print out pretty stuff when enough data has been processed
  853. **  7.  When finished with the input character, output the last code
  854. **  8.  Check the compression ratios.  If enough, remove the original
  855. **      input file, reset the status byte on this file's index entry.
  856. **      Otherwise, simply remove the temp file.
  857. **  9.  Return
  858. **********/
  859.  
  860. compress(struct INDEX *ip)
  861. {
  862. char    fname[MAX_PLEN + MAX_FLEN];
  863. char    tmp_name[MAX_PLEN + MAX_FLEN];
  864. int    fd1;
  865. int    fd2;
  866. char    c;
  867. unsigned    tmp_code;
  868. long    file_len;
  869. int    each_char;
  870. struct    ftime    ft;
  871.  
  872.     if    (exclude(ip->filename))
  873.     {
  874.         tot_skipped++;
  875.         return(SKIP);
  876.         }
  877.     tot_examine++;
  878.  
  879.     window(2, 17, 79, 23);
  880.     clrscr();
  881.     gotoxy(2,1);
  882.     cprintf("%d out of %d files processed. Total %d%% compression, %ld bytes saved\n",
  883.         tot_examine + tot_skipped,
  884.         num_files,
  885.                 total_in_cnt ?
  886.          (int)((100 * (total_in_cnt - total_out_cnt))/total_in_cnt) : 0,
  887.         total_in_cnt - total_out_cnt);
  888.  
  889.     in_cnt = out_cnt = 0L;
  890.     sprintf(fname, "%s%s", only_dir, ip->filename);
  891.     sprintf(tmp_name, "%s%s", only_dir, "COMPRESS.$$$");
  892.     if    ((fd1 = open(fname, O_RDONLY | O_BINARY)) == -1)
  893.     {
  894.         cprintf("??Can't open: %s\n", fname);
  895.         return(ERROR);
  896.     }
  897.     getftime(fd1, &ft);
  898.     file_len = filelength(fd1);
  899.     each_char = max((int)(file_len / 50L), 1);
  900.     unlink(tmp_name);
  901.     if    ((fd2 = open(tmp_name, O_CREAT | O_RDWR | O_BINARY, S_IREAD | S_IWRITE)) == -1)
  902.     {
  903.         cprintf("??Can't open: %s\n", tmp_name);
  904.         close(fd1);
  905.         return(ERROR);
  906.     }
  907.     _write(fd2, "\xcd\x20PCOMPRES", 10);
  908.     init_table();
  909.         stuff(NULL, NULL, RESET);
  910.         get_char(RESET, NULL);
  911.     reset = 0;
  912.  
  913.  
  914.     get_char(fd1, &c);
  915.     oldcode = c;
  916.     in_cnt = 1;
  917.     while (get_char(fd1, &c))
  918.     {
  919.         in_cnt++;
  920.         if    ((tmp_code = get_code(c)) != IN_TABLE)
  921.         {
  922.             stuff(fd2, tmp_code, NORMAL);
  923.             if    (codesused == RESET_CODE)
  924.             {
  925.                 stuff(fd2, RESET_CODE, NORMAL);
  926.                 init_table();
  927.                 reset++;
  928.             }
  929.         }
  930.         if    (!(in_cnt % each_char))
  931.         {
  932.             gotoxy(2, 3);
  933.             percent = (int)((100 * in_cnt)/file_len);
  934.             cprintf("Compressing: %s. %2.2d%% Complete, %u Codes Used", fname, percent, codesused + (reset * MAX_CODE));
  935.             percent = (int)(100 - (100 * out_cnt)/in_cnt);
  936.             gotoxy(2, 4);
  937.             cprintf("Compression of %2.2d%%, %u Bytes Saved.\n", percent, in_cnt - out_cnt );
  938.             gotoxy(2, 6);
  939.             cprintf(" Input Bytes: (%7.7ld)", in_cnt);
  940.             bar_chart((int)(in_cnt / (long)each_char), 0xb1);
  941.             gotoxy(2, 7);
  942.             cprintf("Output Bytes: (%7.7ld)", out_cnt);
  943.             bar_chart((int)(out_cnt / (long)each_char), 0xb2);
  944.          }
  945.     }
  946. /***/
  947.     stuff(fd2, oldcode, FALSE);
  948.     stuff(fd2, NULL, CLOSE);
  949.     in_cnt++;
  950.     percent = (int)(100 - (100 * out_cnt)/in_cnt);
  951.     gotoxy(2, 10);
  952.     close(fd1);
  953.  
  954.     total_in_cnt += in_cnt;
  955.     if    (percent >= min_percent && ((in_cnt - out_cnt) > (long)min_bytes))
  956.     {
  957.         ip->status = COMPRESSED;
  958.         dirty_bit = TRUE;
  959.         setftime(fd2, &ft);
  960.         close(fd2);
  961.         unlink(fname);
  962.         rename(tmp_name, fname);
  963.         total += (in_cnt - out_cnt);
  964.         tot_compressed++;
  965.         total_out_cnt += out_cnt;
  966.         return(COMPRESSED);
  967.     }
  968.     else
  969.     {
  970.         ip->status = LOW;
  971.         dirty_bit = TRUE;
  972.         close(fd2);
  973.         unlink(tmp_name);
  974.         tot_bypassed++;
  975.         total_out_cnt += in_cnt;
  976.         return(LOW);
  977.     }
  978. }
  979.  
  980. check_date(struct INDEX *ip)
  981. {
  982. long    time1 = dostounix(&ip->da, &ip->ti);
  983. long    time2;
  984. long    dtime;
  985.  
  986.     time(&time2);
  987.     dtime = (time2 - time1)/(long)(60L * 60L * 24L);
  988.     return((int)dtime);
  989. }
  990.  
  991. /*********
  992. **    Check a file against a pattern in the exclude file.
  993. **********/
  994. exclude(char *fname)
  995. {
  996. int    exclude_idx;
  997. char    *ptr;
  998. char    *ptr2;
  999.  
  1000.      strupr(fname);
  1001.     for(exclude_idx = 0 ; exclude_idx < exclude_cnt ; exclude_idx++)
  1002.     {
  1003.         ptr2 = exclude_list[exclude_idx];
  1004.         ptr = fname;
  1005.         while (*ptr)
  1006.         {
  1007.             if    (*ptr == *ptr2 || *ptr2 == '?')
  1008.                     {
  1009.                 ptr2++;
  1010.                 ptr++;
  1011.                 continue;
  1012.             }
  1013.             else
  1014.             if    (*ptr2 == '*')
  1015.             {
  1016.                              ptr2++;
  1017.                 if    (!*ptr2)
  1018.                                     return(TRUE);
  1019.                 else
  1020.                 {
  1021.                     while (*ptr && *ptr != *ptr2)
  1022.                         ptr++;
  1023.                     if (!*ptr)
  1024.                         break;
  1025.                 }
  1026.             }
  1027.             else
  1028.                 break;
  1029.                 }
  1030.         if    (!*ptr2)
  1031.             return(TRUE);
  1032.     }
  1033.     return(FALSE);
  1034. }
  1035.  
  1036.